/*
 * Copyright (c) 2020 - present, Inspur Genersoft Co., Ltd.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *       http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.inspur.edp.cef.core.data;

import com.fasterxml.jackson.annotation.JsonIgnore;
import com.inspur.edp.cef.api.RefObject;
import com.inspur.edp.cef.core.data.extendhandler.RootEntityAccExtHandler;
import com.inspur.edp.cef.entity.accessor.base.IAccessor;
import com.inspur.edp.cef.entity.accessor.base.ReadonlyDataException;
import com.inspur.edp.cef.entity.changeset.AbstractModifyChangeDetail;
import com.inspur.edp.cef.entity.changeset.AddChangeDetail;
import com.inspur.edp.cef.entity.changeset.ChangeType;
import com.inspur.edp.cef.entity.changeset.IChangeDetail;
import com.inspur.edp.cef.entity.changeset.InnerUtil;
import com.inspur.edp.cef.entity.changeset.ModifyChangeDetail;
import com.inspur.edp.cef.entity.entity.ICefData;
import com.inspur.edp.cef.entity.entity.IEntityData;
import com.inspur.edp.cef.entity.entity.IEntityDataCollection;
import com.inspur.edp.cef.entity.entity.IMultiLanguageAcc;
import com.inspur.edp.cef.entity.entity.IMultiLanguageData;
import com.inspur.edp.cef.entity.i18n.MultiLanguageInfo;
import com.inspur.edp.cef.spi.entity.resourceInfo.DataTypeResInfo;
import com.inspur.edp.cef.spi.extend.datatype.ICefDataExtend;
import com.inspur.edp.cef.spi.extend.datatype.IDataExtendContainer;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import lombok.var;

public abstract class RootEntityAccessor extends
    com.inspur.edp.cef.entity.accessor.entity.RootEntityAccessor
    implements IDataExtendContainer, IMultiLanguageAcc {

   protected RootEntityAccessor(IEntityData inner) {
      super(inner);
      initExtByInner(inner);
      this.initMultiLanguageInfo(inner);
   }

   //region
   private RootEntityAccExtHandler extHandler;

   private RootEntityAccExtHandler getExtHandler() {
      if (extHandler == null) {
         extHandler = new RootEntityAccExtHandler(this, getIsReadonly());
      }
      return extHandler;
   }

   @Override
   public void addExtend(ICefDataExtend... ext) {
      Objects.requireNonNull(ext, "ext");

      for (ICefDataExtend item : ext) {
         getExtHandler().addExtend(item);
      }
   }

   @Override
   @JsonIgnore
   public List<ICefDataExtend> getExtends() {
      return getExtHandler().getExtendList();
   }

   private void initExtByInner(ICefData inner) {
      if (inner != null && inner instanceof IDataExtendContainer) {
         List<ICefDataExtend> exts = ((IDataExtendContainer) inner).getExtends();
         if (exts != null) {
            for (ICefDataExtend ext : exts) {
               addExtend(ext);
            }
         }
      } else {
         getExtHandler().clear();
      }
   }
   //endregion

   //region get/set Value
   @Override
   public final Object getValue(String s) {
      RefObject<Object> result = new RefObject<>(null);
      if (getExtHandler().getValue(s, result)) {
         return result.argvalue;
      }
      return innerGetValue(s);
   }

   protected abstract Object innerGetValue(String propName);

   @Override
   public final void setValue(String name, Object value) {
      checkReadonly();
      if (getExtHandler().setValue(name, value)) {
         return;
      }
      innerSetValue(name, value);
   }

   protected abstract void innerSetValue(String name, Object value);
   //endregion

   //region
   @Override
   public final ICefData copySelf() {
      return innerCopySelf();
   }

   protected ICefData innerCopySelf() {
      return getInnerData().copySelf();
   }

   @Override
   public final ICefData createChild(String childCode) {
      RefObject<ICefData> result = new RefObject(null);
      if(getExtHandler().createChild(childCode, result)){
         return result.argvalue;
      }
      return innerCreateChild(childCode);
   }

   protected ICefData innerCreateChild(String childCode) {
      throw new RuntimeException();
   }

   @Override
   public final HashMap<String, IEntityDataCollection> getChilds() {
      var childs = innerGetChilds();
      var extedChilds = getExtHandler().getChilds();
      if (extedChilds != null) {
         childs.putAll(extedChilds);
      }
      return childs;
   }

   protected abstract HashMap<String, IEntityDataCollection> innerGetChilds();

   @Override
   public IEntityDataCollection createAndSetChildCollection(String childCode) {
      var refObj = new RefObject<IEntityDataCollection>(null);
      if(getExtHandler().createAndSetChildCollection(childCode, refObj)){
         return refObj.argvalue;
      }
      return innerCreateAndSetChildCollection(childCode);
   }

   //子类实现
   protected IEntityDataCollection innerCreateAndSetChildCollection(String childCode){
      throw new RuntimeException();
   }

   @Override
   protected void acceptChangeCore(IChangeDetail change) {
      checkReadonly();
      getExtHandler().acceptChange(change);
      if (change instanceof AbstractModifyChangeDetail) {
         AbstractModifyChangeDetail changeDetail = (AbstractModifyChangeDetail) change;
         InnerUtil.mergeMultiLanguageInfoToChanges(changeDetail.getMultiLanguageInfos(),
             getMultiLanguageInfos(), changeDetail.getPropertyChanges());
         InnerUtil.mergeChangesToMultiLanguageInfo(changeDetail.getPropertyChanges(),
             getMultiLanguageInfos(), null);
      }

      if( getResInfo() != null) {
         if (change.getChangeType() == ChangeType.Modify) {
            AbstractModifyChangeDetail modifyChangeDetail = (AbstractModifyChangeDetail) change;
            for (Map.Entry<String, Object> entry : modifyChangeDetail.getPropertyChanges()
                .entrySet()) {
               if (entry.getValue() instanceof IChangeDetail)
                  ((IAccessor) getValue(entry.getKey()))
                      .acceptChange((IChangeDetail) entry.getValue());
               else
                  setValue(entry.getKey(), entry.getValue());
            }
            acceptModifyChangeExtendInfos((ModifyChangeDetail) modifyChangeDetail);
            return;
         } else {
            throw new IllegalArgumentException("change");
         }
      }
   }

   protected void acceptModifyChangeExtendInfos(ModifyChangeDetail modifyChangeDetail){
      if(modifyChangeDetail.getChildChanges()==null||modifyChangeDetail.getChildChanges().size()==0)
         return;
      for (Map.Entry<String, Map<String, IChangeDetail>> childChange : modifyChangeDetail
          .getChildChanges().entrySet()) {
         for (Map.Entry<String, IChangeDetail> item : childChange.getValue().entrySet()) {
            IEntityDataCollection childCollection = getChilds().get(childChange.getKey());
            if(childCollection == null){
               throw new IllegalArgumentException("变更集中的子表变更的子表编号不存在:"+childChange.getKey());
            }
            if(item.getValue().getChangeType()==ChangeType.Added) {
               //TODO: copy可能导致acc内部出现多层acc, 需要修改
               childCollection
                   .add((IEntityData) ((AddChangeDetail)item.getValue()).getEntityData().copy());
            } else if(item.getValue().getChangeType()==ChangeType.Deleted) {
               childCollection.remove(item.getValue().getDataID());
            }else{
               IEntityData childData = childCollection.getItem(item.getKey());
               if(childData == null){
                  throw new RuntimeException("修改的子表不存在:" + item.getKey());
               }
               ((IAccessor) childData).acceptChange(item.getValue());
            }
         }
      }
   }

   protected DataTypeResInfo getResInfo(){
      return null;
   }

   @Override
   protected void onInnerDataChange() {
      super.onInnerDataChange();
      if(getExtHandler().getExtendList().isEmpty()) {
         initExtByInner(getInnerData());
      } else {
         getExtHandler().onInnerDataChanged();
      }
      this.initMultiLanguageInfo(this.getInnerData());
   }

   @Override
   public ICefData copy() {
      RootEntityAccessor result = (RootEntityAccessor) super.copy();

      result.extHandler = (RootEntityAccExtHandler) getExtHandler().copy(result);
//      List<ICefDataExtend> currentExts = getExtends();
//      if (currentExts != null) {
//         result.addExtend(currentExts.toArray(new ICefDataExtend[currentExts.size()]));
//      }

      Map<String, MultiLanguageInfo> multiLanguageInfoMap = getMultiLanguageInfos();
      if (multiLanguageInfoMap != null) {
         multiLanguageInfoMap.entrySet().forEach(item -> result.getMultiLanguageInfos().put(item.getKey(), item.getValue()));
      }

      return result;
   }

   //endregion

   // region MultiLanguageInfo
   @JsonIgnore
   private Map<String, MultiLanguageInfo> multiLanguageInfos;

   @JsonIgnore
   @Deprecated
   public Map<String, MultiLanguageInfo> getMultiLanguageInfos() {
      if (this.multiLanguageInfos == null) {
         this.multiLanguageInfos = new HashMap<>();
      }
      return multiLanguageInfos;
   }

   private void setMultiLanguageInfos(Map<String, MultiLanguageInfo> infos) {
      this.multiLanguageInfos = infos;
   }

   private void initMultiLanguageInfo(IEntityData inner) {
      Map<String, MultiLanguageInfo> infos = null;
      if (inner instanceof IMultiLanguageData) {
         IMultiLanguageData cefDataBase = (IMultiLanguageData) inner;
         infos = new HashMap<>(cefDataBase.getMultiLanguageInfos().size(), 1);
         for(Map.Entry<String, MultiLanguageInfo> entrySet : cefDataBase.getMultiLanguageInfos().entrySet()){
            // 此处克隆，保持回滚能力
            infos.put(entrySet.getKey(), entrySet.getValue().clone());
         }
      }
      this.setMultiLanguageInfos(infos);
   }
   // endregion
   public IEntityDataCollection innerCreateAndSetChildCollection(String childNodeCode, String code, AccessorCollection accessorCollection, IEntityDataCollection entityDataCollection) {
      if (code != null && !code.isEmpty() && childNodeCode.equals(code)) {
         AccessorCollection collection = accessorCollection;
         collection.setParent(this);
         entityDataCollection = collection;
         return entityDataCollection;
      }
      throw new RuntimeException();
   }

   @Override
   public void setMultiLang(String propName, String langCode, Object value) {
      checkReadonly();

      Object orgValue = getMultiLang(propName,langCode);
      raiseMultiLangChanging(propName, langCode, value, orgValue);
      IMultiLanguageAcc.super.setMultiLang(propName, langCode, value);
   }
}
